# -*- coding: utf-8 -*-

import websocket
import json
from typing import Dict, Any
import uuid
import hashlib
import getpass
import socket
import threading
import requests
import time
import logging
from logging.handlers import TimedRotatingFileHandler
import queue
import os
import sys
from processor.autotest.log import queue_autotest_log
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# queue_slave_log = queue.Queue()
logger = logging.getLogger("slave")
server_addr: str = "localhost:8080"
try_connect_time = 300
ws: websocket.WebSocket = None
sid: str = ''  # 执行机ID
tasks: Dict[Any, Dict[str, Any]] = {}  # task_id -> {'stop_task': False, 'thread': t, 'queue_autotest_log': Queue}, 用于执行时通信/处理
token: str = ''
request_header: dict = {}
# register_file = f"{os.path.expanduser('~')}/.tdp"
register_file = f"./.tdp"


def stop_task(data: dict):
    task_id = data['task_id']
    if task_id not in tasks:
        logger.info(f'终止测试任务 {task_id} -> 未在本机任务列表中！')
        srequest(method='patch', url=f'/tdp/slave?c=stopTaskStatus&task_id={task_id}')
    else:
        t: threading.Thread = tasks[task_id].get('thread')
        if t.is_alive():
            tasks[task_id]['stop_task'] = True
            logger.info(f'终止测试任务 {task_id} -> 已发送终止信号, 正在等待线程终止！')
            while True:
                if not tasks.get(task_id) or not tasks[task_id]['thread'].is_alive():
                    logger.info(f'终止测试任务 {task_id} -> 线程已终止！')
                    srequest(method='patch', url=f'/tdp/slave?c=stopTaskStatus&task_id={task_id}')
                    break
                time.sleep(0.3)
        else:
            logger.info(f'终止测试任务 {task_id} -> 任务已终止！')
            srequest(method='patch', url=f'/tdp/slave?c=stopTaskStatus&task_id={task_id}')


def newAutotestTask(data: dict):
    task_id = data['id']
    log = True
    # headers = {
    #     'token': token,
    #     'sid': sid
    # }

    def autotest_log_post():
        while log:
            try:
                d = queue_autotest_log.get(timeout=1)  # test_result / run_log
                queue_autotest_log.task_done()
                log_type = d['type']
                request_params = d['request_param']
                data = d['data']
                if log_type == 'text':
                    requests.post(
                        f"http://{server_addr}/tdp/slave?c=autotestLog&type=run_log&task_id={task_id}"
                        f"&{request_params}",
                        json=data, headers=request_header, verify=False
                    )
                elif log_type == 'snapshot':
                    file_name_show = d['file_name']
                    d = {'file_name_show': file_name_show}
                    with open(data, 'rb') as screen_file:
                        files = {
                            'file': screen_file,
                        }
                        requests.post(
                            f"http://{server_addr}/tdp/slave?c=autotestLog&type=run_log_snapshot&task_id={task_id}"
                            f"&{request_params}",
                            files=files, data=d, headers=request_header, verify=False
                        )
                    os.remove(data)
                    logger.info(f'图像<{file_name_show}>已上传，文件:{data} 已删除！')
            except:
                pass
            # time.sleep(0.3)
    t = threading.Thread(target=autotest_log_post)
    t.start()
    test_data = get_task_data(
        f'http://{server_addr}/tdp/slave?c=taskInfo&task_id={task_id}'
        f'&testset_id={data["testset_id"]}&sid={sid}'
    )['data']
    test_data['task_id'] = task_id
    test_data['sid'] = sid
    test_data['tdp_server_addr'] = server_addr
    test_data['tdp_token'] = token
    from processor.autotest import Autotest
    ts = Autotest(test_data, task_data=tasks[task_id])
    try:
        ts.plan()
    except:
        raise
    finally:
        queue_autotest_log.join()
        log = False
        t.join()


def re_register(data):
    global token, request_header, sid, register_file
    token = data['token']
    sid = data['sid']
    request_header['token'] = token
    request_header['sid'] = sid
    with open(register_file, 'w') as f:
        json.dump(data, f)


def get_task_data(url: str) -> dict:
    rec = requests.get(url=url, headers=request_header, verify=False)
    j = rec.json()
    logger.info('获取测试数据：' + json.dumps(j, ensure_ascii=False))
    return j


def register():
    global sid, register_file, server_addr
    if os.path.exists(register_file):
        with open(register_file, 'r') as f:
            sinfo = json.load(f)
            logger.info(f'load conf: {sinfo}')
    else:
        sinfo = {}
    with open('./conf.json', 'r') as f:
        conf = json.load(f)
        tdp_user = conf.get('tdp_user', '')
    server_addr = conf.get('server_addr', 'localhost:8080')
    ws_url = f"ws://{server_addr}/tdp/slave/websocket"
    host_info = get_host_info()
    header = [
        f"Sid:{sinfo.get('sid', '')}",
        f"Tdp_user:{tdp_user}",
        f"Open_action:register",
        f"Host_info:{json.dumps(host_info, ensure_ascii=False)}"
    ]
    logger.info(f'尝试连接到服务器 {ws_url}, header: {header}')
    global ws
    import ssl
    # context = ssl._create_unverified_context()
    # ssl._create_default_https_context = ssl._create_unverified_context
    # ws = websocket.create_connection(ws_url, header=header, sslopt={'context':context, 'verify':False}, verify=False)
    ws = websocket.create_connection(ws_url, header=header, sslopt={"cert_reqs": ssl.CERT_NONE})
    # ws = websocket.create_connection(ws_url, header=header)
    logger.info('成功连接到服务器')


def try_reconnect():
    t = time.time()
    while True:
        try:
            register()
            break
        except ConnectionResetError:
            time.sleep(1)
        except ConnectionRefusedError:
            time.sleep(1)
        if time.time() - t > try_connect_time:
            break


def run_log_post():
    pass


def restart_ready():
    pass


def get_host_info():
    host_info = {
        'username': getpass.getuser(),
        'hostname': socket.gethostname()
    }
    return host_info


class TdpLogHandler(logging.Handler):
    def emit(self, record):
        pass
        # queue_slave_log.put(record)


def set_slave_log():
    log_path = './log'
    if not os.path.isdir(log_path):
        os.mkdir(log_path)
    # 文件日志
    log_file = f"{log_path}/slave.log"
    formatter = logging.Formatter(
        '%(asctime)s %(threadName)s %(filename)s %(funcName)s [%(levelname)s]: #  %(message)s')
    file_handler = TimedRotatingFileHandler(log_file, when="d", interval=1, backupCount=30, encoding='utf-8')
    file_handler.setFormatter(formatter)  # 可以通过setFormatter指定输出格式

    # 控制台日志
    console_handler = logging.StreamHandler()
    console_handler.formatter = formatter  # 也可以直接给formatter赋值

    # TDP日志
    tdp_handler = TdpLogHandler()
    tdp_handler.formatter = formatter

    # 为logger添加的日志处理器
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    # logger.addHandler(tdp_handler)

    # 指定日志的最低输出级别，默认为WARN级别
    # DEBUG，INFO，WARNING，ERROR，CRITICAL
    logger.setLevel(logging.INFO)


def marry(method: str, data: dict):
    """
    匹配 服务器下发指令 对应的处理方法
    :param method: 处理方法
    :param data: 下发指令附带的数据
    :return:
    """
    try:
        f = globals().get(method)
        if not f:
            logger.error(f'未找到方法 {method}')
        if method.startswith('new') and method.endswith('Task'):
            t = threading.Thread(target=wrap_task_method, args=(f, data))
            tasks[data['id']] = {'stop_task': False, 'thread': t}
        else:
            t = threading.Thread(target=f, args=(data,))
        t.start()
    except:
        raise


def wrap_task_method(func, data: dict):
    task_id = data['id']
    try:
        func(data)
    except:
        logger.exception(f'AutotestTask task id:{task_id}')
    logger.info(f'AutotestTask task id:{task_id},线程结束！')
    requests.post(
        f'http://{server_addr}/tdp/slave?c=autotestLog&type=task_result&task_id={task_id}',
        headers=request_header, verify=False
    )  # 任务结束
    tasks.pop(task_id)


def srequest(method: str, url: str):
    global request_header
    url = f'http://{server_addr}' + url
    logger.info(f'{method.upper()} request to {url}')
    try:
        res = requests.request(method=method, url=url, headers=request_header, verify=False)
    except Exception as e:
        logger.error(f'请示发送失败：{method.upper()} {url} -> {repr(e)}')
    else:
        logger.info(f'response {method.upper()} {url}: {res.text}')
        return res.json()


class WebsocketConn(websocket.WebSocketApp):
    def on_message(self, message):
        print(message)

    def on_error(self, error):
        print(error)

    def on_close(self):
        print("### websocket closed ###")

    def on_open(self):
        def run(*args):
            pass

        threading.Thread(target=run).start()


if __name__ == '__main__':
    websocket.enableTrace(True)
    ws = WebsocketConn(f'ws://{server_addr}/tdp/slave/websocket').run_forever()


